/*
 * Funny Language - a free style programming language.
 * Copyright (C) 2015 by fanguangping (fanguangping@163.com)
 * dfa.c
 */

#include <stdlib.h>
#include <stdio.h>
#include "matcher.h"
#include "../premise/lexer.h"
#include "../engine/fpengine.h"
#include "../config/sys.h"
#include "macroexpander.h"

String match_one_arg(Statement *source) {
	String symbol = get_arg_name(source);

	String lex = expand_lex(symbol);
    if (lex != NULL) {
        return lex;
    }

	/*
	Mapping *mapping = NULL;
	String s = NULL;
	QueueNode *node = NULL;

	Queue *mappings = get_mappings(source);
	if (mappings != NULL) {
		//MAPPINGS
		node = mappings->head;
		while (node != NULL) {
			mapping = (Mapping *) node->element;
			if (mapping->sys != NULL) {
				node = node->next;
				continue;
			}

			if (equal_arg(mapping->sourceStatement, symbol)) {
				s = extract(mapping->targetStatement);
				return s;
			}

			if (mapping->functionName != NULL && strcmp(symbol, mapping->functionName) == 0) {
				return mapping->functionName;
			}

			node = node->next;
		}
	}
	*/

	return NULL;
}

String match_original(Statement *source) {
	Queue *mappings = get_mappings(source);
    QueueNode *node = NULL;
	Mapping *mapping = NULL;

	if (mappings != NULL) {
		//MAPPINGS
		node = mappings->head;
		while (node != NULL) {
			mapping = (Mapping *) node->element;
			if (mapping->sys != NULL) {
				node = node->next;
				continue;
			}

			if (equal_statement(mapping->sourceStatement, source)) {
				return extract(mapping->targetStatement);
			}

			node = node->next;
		}
	}

	return extract(source);
}

String match_loop_statement(Statement *loop, Queue *replaceQueue, int matchTimes) {
    int i = 0;
    int index = 0;
    QueueNode *node = NULL;
    Statement *s = NULL;
    Statement *st = NULL;
    String result = init_string();
    Queue *loopQueue = NULL;
    QueueNode *loopQueueNode = NULL;

    for (i = 0; i < matchTimes; i++) {
        if (loop != NULL) {
            node = loop->children->head;
            while (node != NULL) {
                result = dyna_strcat(result, " ");

                s = (Statement *) node->element;
                if (s->type == ARG) {
                    index = 0;

                    loopQueue = find_values(replaceQueue, get_arg_name(s));
                    loopQueueNode = loopQueue->head;
                    while (loopQueueNode != NULL) {
                        if (index == i) {
                            st = (Statement *) get_queue_element(loopQueue, index);
                            if (empty_statement(st)) {
                                return NULL;
                            }
                            result = dyna_strcat(result, match_internal(st));
                            break;
                        }
                        index++;
                        loopQueueNode = loopQueueNode->next;
                    }
                } else if (!equal_sym(s, "...")){ //ELLIPSIS_STR
                    result = dyna_strcat(result, to_string_statement(s));
                }
                node = node->next;
            }
        }
    }

    return result;
}

String eval_normal(Mapping *mapping, Queue *replaceQueue) {
    String result = init_string();
    QueueNode *node = NULL;
    Statement *s = NULL;

    String loopBuffer = NULL;
    int matchTimes = 0;

    node = mapping->targetStatement->children->head;
    while (node != NULL) {
        s = (Statement *) node->element;

        switch (s->type) {
            case SYM:
                result = dyna_strcat(result, to_string_statement(s));
                result = dyna_strcat(result, " ");
                break;

            case ARG:
                result = dyna_strcat(result, match_internal((Statement *) find_value(replaceQueue, get_arg_name(s))));
                result = dyna_strcat(result, " ");
                break;

            case LOOP:
                matchTimes = count_loop_times_by_queue(s, replaceQueue);
                loopBuffer = match_loop_statement(s, replaceQueue, matchTimes);
                if (loopBuffer != NULL) {
                    result = dyna_strcat(result, loopBuffer);
                }
                break;

            default:
                break;
        }

        node = node->next;
    }

    return result;
}

String match_internal(Statement *source) {
	QueueNode *node = NULL;
	Mapping *mapping = NULL;
	String result = NULL;
	State *start = NULL;
	Queue *replaceQueue = NULL;
	Statement *expanded = NULL;

	// match one arg
	if (count_arg_elements(source) == 1) {
		result = match_one_arg(source);
		if (result != NULL) {
			return result;
		}
	}

	// match orignal
	if (statement_with_symbols(source)) {
		result = match_original(source);
		if (result != NULL) {
			return result;
		}
	}

	Queue *mappings = get_mappings(source);
	if (mappings != NULL) {
		node = mappings->head;
		while (node != NULL) {
			mapping = (Mapping *) node->element;
			replaceQueue = init_queue();

			start = test_match(mapping, source, replaceQueue);
			if (start == NULL) {
				node = node->next;
				continue;
			}
			if (start->accept) {
				if (mapping->isMacro) {
					expanded = expand_statement(mapping, replaceQueue);
					String string = to_string_statement(expanded);
					printf("expanded: %s\n", string);
					return match_internal(expanded);
				}

				if (mapping->sys != NULL) {
					return eval_sys(mapping, replaceQueue);
				} else {
					result = eval_normal(mapping, replaceQueue);
				}
			}

			node = node->next;
		}
	}

	if (result == NULL) {
		printf("Cannot find mapping for statement: %s\n", to_string_statement(source));
	}

	return result;
}
